/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * assign4
 * Copyright (C)  Michael 2008 <mikey.morckos@gmail.com>
 * 
 * assign4 is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * assign4 is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Q2.h"

/* GLOBALS */
const int POINT_COUNT = 10;

CvMat *points1, *points2;
CvMat* fundamental_matrix;
CvMat* status;
CvMat* single_point;
CvMat* result;

CvMat tmp_fundamental;
CvMat tmp_points1, tmp_points2;
CvMat tmp_single_point;
CvMat tmp_result;

double point_arr1 [POINT_COUNT] [2];
double point_arr2 [POINT_COUNT] [2];
double funda_arr [3] [3];
double result_arr [3] [1];
float tmp_x1 = 0, tmp_y1 = 0, tmp_x2 = 0, tmp_y2 = 0;

IplImage *img1, *img2;
const char *name1, *name2;
int img_height, img_width;

/* PROTOTYPES */
void compute_Fundamental(bool);
void init();
void get_Epipolar(bool);
void draw_Line(int, int, int, int, int);
void on_Mouse_Clicked_1(int, int, int, int, void*);
void on_Mouse_Clicked_2(int, int, int, int, void*);

/* INITIALIZATIONS */
void init() {
	
	name1 = "Image-1";
    name2 = "Image-2";
	
	img1 = 0;
	img2 = 0;
	
	img1 = cvLoadImage("painting1_1060.jpg", 1);
	img2 = cvLoadImage("painting2_1061.jpg", 1);
	
	img_height = img1->height;
	img_width = img1->width;
	
	cvNamedWindow(name1, 3);
	cvNamedWindow(name2, 3);
	
	cvShowImage(name1, img1);
	cvShowImage(name2, img2);
	
	/* DEFINING MOUSE EVENTS */
	cvSetMouseCallback(name1, on_Mouse_Clicked_1, 0);
	cvSetMouseCallback(name2, on_Mouse_Clicked_2, 0);
	
	/* INIT MATRICES */
	tmp_points1 = cvMat(POINT_COUNT, 2, CV_64FC1, point_arr1);
	tmp_points2 = cvMat(POINT_COUNT, 2, CV_64FC1, point_arr2);
		
	points1 = &tmp_points1;
	points2 = &tmp_points2;
		
	tmp_fundamental = cvMat(3, 3, CV_64FC1, funda_arr);
	fundamental_matrix = &tmp_fundamental;
	
	tmp_result = cvMat(3, 1, CV_64FC1, result_arr);
	result = &tmp_result;
	
	cout << img_height << " " << img_width << endl;
		
//	compute_Fundamental();
}	

/* FUNDAMENTAL MATRIX */
void compute_Fundamental (bool flag) {
	
	FILE *data = fopen("corresponds", "r");
  
	for(int i = 0; i < POINT_COUNT; i++) {
		fscanf(data, "%f %f %f %f", &tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
		
		/* DEPENDING ON THE FLAG THE FUNDAMENTAL IS COMPUTED
		   WITH RESPECT TO THE CLICKED IMAGE 
		*/
		if(flag) {
			point_arr1[i][0] = (double) tmp_x1;
	        point_arr1[i][1] = (double) tmp_y1;
		
	        point_arr2[i][0] = (double) tmp_x2;
	        point_arr2[i][1] = (double) tmp_y2;
		}
		else {
			point_arr2[i][0] = (double) tmp_x1;
	        point_arr2[i][1] = (double) tmp_y1;
		
	        point_arr1[i][0] = (double) tmp_x2;
	        point_arr1[i][1] = (double) tmp_y2;
		}
		
	}
   
	fclose(data);
		
	//cout << "EE... " << CV_ARE_SIZES_EQ(points1, points2) << endl;
	//cout << "EE... " << CV_IS_MAT(points1) << endl;
	//cout << "EE... " << CV_IS_MAT(points2) << endl;
	
    /* COMPUTING THE FUNDAMENTAL MATRIX */	
	int fm_count = cvFindFundamentalMat(points1, points2, fundamental_matrix);
	/* INDICATOR OF SUCCESSFUL COMPUTATION */
	cout << "Fundamental " << fm_count << endl;
}

/* ACQUIRING A POINT FROM IMAGE-1 */
void on_Mouse_Clicked_1 (int event_ID, int x, int y, int flags, void *param) {
	
	double point_arr[3][1];
	CvMat tmp;
	CvScalar s;
	
    if(event_ID == CV_EVENT_LBUTTONDBLCLK) {
		point_arr[0][0] = (double) x;
	    point_arr[1][0] = (double) y;
		point_arr[2][0] = 1.0;
		
       	tmp = cvMat(3, 1, CV_64FC1, point_arr);
        single_point = &tmp;
		
		cout << x << " " << y << endl;		
/*
		s = cvGet2D(img1, y, x);
		s.val[0] = 255; s.val[1] = 255; s.val[2] = 255;
		cvSet2D(img1, y, x, s);
		cvShowImage(name1, img1);
*/
		compute_Fundamental(1);
		get_Epipolar(1);
	}
}

/* ACQUIRING A POINT FROM IMAGE-2 */
void on_Mouse_Clicked_2 (int event_ID, int x, int y, int flags, void *param) {
	
	double point_arr[3][1];
	CvMat tmp;
	CvScalar s;
	
	if(event_ID == CV_EVENT_LBUTTONDBLCLK) {
       	point_arr[0][0] = (double) x;
       	point_arr[1][0] = (double) y;
       	point_arr[2][0] = 1.0;
	
       	tmp = cvMat(3, 1, CV_64FC1, point_arr);
        single_point = &tmp;
		
		cout << x << " " << y << endl;
		
		compute_Fundamental(0);
		get_Epipolar(0);
	}
}

/* MULTIPLYING THE FUNADAMENTAL MATRIX WITH THE CLICKED PIXEL */
void get_Epipolar(bool key) {
	
	double a = 0, b = 0, c = 0;
	int x1 = 0, y1 = 0, x2 = 0, y2 = 0, tmp = 0;
	
	/* MULTIPLYING THE FUNDAMENTAL BY THE CHOSEN POINT */
	cvMatMul(fundamental_matrix, single_point, result);
	
	a = cvmGet(result, 0, 0);
	b = cvmGet(result, 1, 0);
	c = cvmGet(result, 2, 0);
	
	for(int i = 0; i < img_width; i++) {
		tmp = (c - (a * i)) / b;
		if(tmp >= 0 && tmp < img_height) {
			x1 = i;
			y1 = tmp;
			break;
		}	
	}

    for(int i = img_width - 1; i >= 0; i--) {
		tmp = (c - (a * i)) / b;
		if(tmp >= 0 && tmp < img_height) {
			x2 = i;
			y2 = tmp;
			break;
		}	
	}	
	
	draw_Line(x1, y1, x2, y2, key);
}

/* METHOD FOR DRAWING THE LINE */
void draw_Line(int x1, int y1, int x2, int y2, int key) {
	
	if(key) {
		cvLine(img2, cvPoint(x1, y1), cvPoint(x2, y2), cvScalar(255, 255, 255), 1);
        cvShowImage(name2, img2);
	}
	else {
		cvLine(img1, cvPoint(x1, y1), cvPoint(x2, y2), cvScalar(255, 255, 255), 1);
		cvShowImage(name1, img1);
	}	
}
	

/* CLASS */
Q2 :: Q2 () 

: img_height(0),
  img_width(0)
{
	set_border_width(5);
}

Q2 :: ~Q2() {
	
	cout << "Closing..." << endl;
	
	cvReleaseImage(&img1);
    cvReleaseImage(&img2);

    cvDestroyWindow(name1);
	cvDestroyWindow(name2);	
}
	

/* MAIN */
int main (int argc, char *argv[]) {

    Gtk::Main kit(argc, argv);
    
    init();
    Q2 editor;
    Gtk::Main::run(editor);

    return 0;
}

